iT邦幫忙

2023 iThome 鐵人賽

DAY 20
0
自我挑戰組

用ChatGPT詠唱來完成工作與點亮前後端技能樹系列 第 20 篇

【Day20】ChatGPT請教教我:React入門(六)- ref?useRef!forwardRef!useImperativeHandle!

  • 分享至 

  • xImage
  •  

目前這個系列已挑選ChatGPT、前端&React相關章節出版成書!
感謝大家的閱讀與支持!
《用 ChatGPT 詠唱來點亮 React & 前端技能樹》
歷經重新整理、校正、編排、與修改後,讓章節內容之間的銜接更平順,除了補充上更多的解說之外,甚至有部分章節為重新撰寫的內容,讓理解變得更輕鬆!
目前已在天瓏書局、博客來、金石堂等通路都買得到了! 也有電子書的選擇!
歡迎各位點擊連結選購,謝謝大家~
https://ithelp.ithome.com.tw/upload/images/20240419/20135750lQM12by6v1.jpg


和hook有關主題寫了三篇,其實不在我預料之內
也可以說四篇啦,useState也是一個hook

OK總之,React提供了各種的hook
工具的出現是為了解決問題的,不用因為很多類型學不完而太焦慮

微微回顧上一篇,ChatGPT對於useRef的提及如下

useRef

  • useRef 可以用來獲得 DOM 元素的引用,也可以用來儲存任意可變值。

我們先來了解一下,React平時在開發的時候,和DOM的關係是什麼
為什麼要特別獲得DOM元素呢?

DOM 操作與React的關係

https://ithelp.ithome.com.tw/upload/images/20230928/20135750sPH5ZFaAru.png

當然可以!首先,讓我來解釋一下 DOM 的概念。

DOM(Document Object Model)翻譯成中文就是「文件物件模型」,它是一種表示網頁文件結構的方式。在網頁上,每一個 HTML 元素(比如說標題、段落、按鈕等等)都被看作是一個物件,透過 DOM,我們可以透過程式碼來操控這些物件,改變它們的內容、樣式、屬性等等。

當你使用 React 開發時,React 會幫助你管理 DOM。React 使用虛擬 DOM(Virtual DOM)來追蹤和記錄 DOM 的變化,這樣可以極大地提升應用程式的效能。

比方說,當你在 React 中修改一個元素的狀態(state),React 會計算出新的虛擬 DOM,然後對比新舊虛擬 DOM 的差異,僅僅更新有變化的部分,而不是整個網頁重新渲染。這個過程讓 React 非常高效。

舉個例子:

假設你在 React 中有一個按鈕元素:

import React, { useState } from 'react';

function MyComponent() {
  const [count, setCount] = useState(0);

  const handleClick = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <button onClick={handleClick}>點我增加數字</button>
      <p>目前數字:{count}</p>
    </div>
  );
}

export default MyComponent;

當你點擊這個按鈕時,handleClick 函式會被呼叫,這會觸發 React 更新相應的虛擬 DOM,然後只更新變化的部分,也就是計數器的數字,而不會重新渲染整個網頁。

這樣的操作方式使得 React 在處理大型、複雜的應用程式時非常高效,因為它只會處理必要的部分,而不是整個頁面。

自從開始使用React之後,和原生JS或jQuery相比,幾乎是沒有在操縱DOM元素物件了

React建立和操作的是「虛擬DOM」
在jQuery或是原生JS中,要管理顯示的UI,動不動就就要取得DOM

請ChatGPT來解說和示範
https://ithelp.ithome.com.tw/upload/images/20230928/20135750MmHa0VbtRq.png

好的,現在來談談使用原生 JavaScript 或 jQuery 時,以及它們和 DOM 的操作關係。

  1. 原生 JavaScript:

使用原生 JavaScript 時,你會直接透過 JavaScript 來選取、操作 DOM 元素。例如,在上述的例子中,如果你想要實現一個計數器,你會這樣做:

document.addEventListener('DOMContentLoaded', function() {
  var count = 0;

  var button = document.getElementById('myButton');
  var counter = document.getElementById('myCounter');

  button.addEventListener('click', function() {
    count++;
    counter.innerText = '目前數字:' + count;
  });
});

這段程式碼使用 document.getElementById 來獲取按鈕和計數器元素,然後透過事件監聽器來設置按鈕點擊事件的處理函式。當按鈕被點擊時,計數器的內容將被更新。

  1. jQuery:

如果你使用 jQuery,則可以更為簡潔地進行相同的操作:

$(document).ready(function() {
  var count = 0;

  $('#myButton').click(function() {
    count++;
    $('#myCounter').text('目前數字:' + count);
  });
});

這段程式碼使用 jQuery 提供的簡潔的選擇器語法來選取元素,並使用 .click 來設置按鈕點擊事件的處理函式。當按鈕被點擊時,計數器的內容將被更新。

總的來說,使用原生 JavaScript 或 jQuery 時,你需要手動選擇 DOM 元素,然後透過 JavaScript 來操作它們的屬性、內容等。相對於 React,這樣的操作方式可能會更為繁瑣,因為你需要管理 DOM 的變化,並確保更新的正確性。而 React 通過虛擬 DOM 簡化了這個過程,使得開發者可以更專注於應用程式的邏輯和組件的開發。

先取得DOM元素,然後針對元素內部的數值、方法去做存取或更動
但在react中是:

const [count, setCount] = useState(0);
return (
  <div>
    <button onClick={()=> setCount(count + 1)}>點我增加數字</button>
    <p>目前數字:{count}</p>
  </div>
);

JSX指定好之後,只要管理好狀態變化,就可以隨時對渲染出的元件做出對應的改變
但有沒有什麼時候,是「必須」取得DOM再來直接對DOM做操作的呢
https://ithelp.ithome.com.tw/upload/images/20230928/20135750Qis65IANC4.png

在 React 的開發中,通常盡量避免直接操作或更新 DOM,而是透過 React 的狀態(state)和屬性(props)來管理元素的狀態和外觀。這是因為 React 使用虛擬 DOM 來追蹤 DOM 變化,並根據變化來做出更新,這樣可以提升應用程式的效能。

然而,有時候還是會遇到需要直接操作或更新 DOM 的情況,例如:

  1. 整合第三方函式庫: 有些第三方函式庫可能需要直接操作 DOM,例如地圖、視頻播放器等等。在這種情況下,你可能需要在 React 應用程式中使用 ref 來引用特定的 DOM 元素,以便進行操作。
  2. 特殊效果或動畫: 有時候,一些特殊的效果或動畫可能需要直接修改 DOM 的樣式。你可以透過 React 的 ref 或者使用 React 庫中的動畫函式庫來實現這些效果。
  3. 與非受控元素互動: 在某些情況下,你可能需要與非受控元素(uncontrolled elements)互動,這些元素不受 React 狀態的控制。這時,你可能需要使用原生 JavaScript 或者 jQuery 來處理這些元素。

儘管如此,在直接操作或更新 DOM 的情況下,也要謹慎使用,盡可能避免影響 React 的狀態管理,以免引起不一致性或效能問題。如果有需要操作 DOM 的情況,建議在生命週期函式(如 componentDidMount、componentDidUpdate)中進行,並注意適當的清理和銷毀操作。

所以,少數情況下,還是有「必須」直接操作DOM的時候
除了這些情況之外,React都不鼓勵這麼做

我們請ChatGPT給點例子好了
https://ithelp.ithome.com.tw/upload/images/20230928/20135750FwMyosEgpw.png

當然,這裡提供一個特殊例子,示範在 React 中必須直接操作 DOM 的情況。

假設你需要在一個表單中,當用戶點擊提交按鈕時,自動將焦點設置到下一個輸入欄位,這種情況下可能需要直接操作 DOM。以下是一個範例程式碼:

import React, { useRef } from 'react';

function MyComponent() {
  const inputRef = useRef(null);

  const handleSubmit = (e) => {
    e.preventDefault();
    // 直接操作 DOM,將焦點設置到下一個輸入欄位
    inputRef.current.nextSibling.focus();
  };

  return (
    <form>
      <input type="text" ref={inputRef} />
      <input type="text" />
      <button onClick={handleSubmit}>提交</button>
    </form>
  );
}

export default MyComponent;

在這個範例中,我們使用了 useRef 來建立一個對第一個輸入欄位的參考(ref)。當用戶點擊提交按鈕時,handleSubmit 函式會被呼叫,並透過 inputRef.current.nextSibling 來獲取下一個輸入欄位,然後將焦點設置到該輸入欄位。

這是一個需要直接操作 DOM 的特殊情況,因為 React 並不提供一個直接的方式來設置下一個元素的焦點。在這種情況下,我們可以使用 useRef 來建立一個參考,然後透過 current 屬性來獲取 DOM 元素,進行需要的操作。

出現ㄌ !useRef!

useRef

像上面ChatGPT給的範例,要指定「focus」這個動作的時候
在原生/ jQuery中,取得dom之後就用 .foucs() 來指定

<input id="nameInput" value="">
<script>
	$("#nameInput").foucs() // jQuery
	document.getElementById("nameInput".foucs() // 原生寫法
<script>

這時就會突然發現,欸?react沒辦法透過attr等方式,去指定input現在是否foucs

確實就需要「先獲取DOM元素、再直接對DOM進行操作」
接著我們請ChatGPT提供有關更多useRef的教學
https://ithelp.ithome.com.tw/upload/images/20230928/20135750Yxac77ww9q.png)

當然!useRef 是 React 提供的一個 hooks,用於在函式型組件中創建一個可變的物件參考。它主要用於存儲和訪問 DOM 元素或其他 React 元素。

以下是關於 useRef 的深入介紹:

1. 創建一個 useRef:

import React, { useRef } from 'react';

function MyComponent() {
  const myRef = useRef(null);

  // myRef.current 將會是一個物件,可以用於存儲任何你想要的值
  // 初始值為 null

  return <div ref={myRef}>這是一個 DOM 元素</div>;
}

export default MyComponent;

在這個範例中,我們使用 useRef 創建了一個 myRef。它是一個可變的物件,初始值為 null。

2. 連接到 DOM 元素:

function MyComponent() {
  const myRef = useRef(null);

  useEffect(() => {
    // myRef.current 會指向這個 div 元素
    myRef.current.style.backgroundColor = 'lightblue';
  }, []); // 空依賴陣列表示只在組件掛載時執行一次

  return <div ref={myRef}>這是一個 DOM 元素</div>;
}

在這個範例中,我們使用了 useEffect 來在組件掛載後執行一段程式碼。在這個程式碼中,我們可以透過 myRef.current 來訪問這個 DOM 元素,並對它進行操作。

3. 存儲可變值:

function MyComponent() {
  const countRef = useRef(0);

  useEffect(() => {
    countRef.current = countRef.current + 1;
    console.log(`countRef 的值為:${countRef.current}`);
  }, []);

  return <div>這是一個 DOM 元素</div>;
}

在這個範例中,我們將一個計數器存儲在 countRef 中,並在每次組件掛載後增加它的值。我們可以透過 countRef.current 來訪問和修改這個計數器。

4. 保留對 React 元素的引用:

function MyComponent() {
  const myRef = useRef(null);

  const handleClick = () => {
    myRef.current.focus();
  };

  return (
    <div>
      <input ref={myRef} type="text" />
      <button onClick={handleClick}>將焦點設置到輸入欄位</button>
    </div>
  );
}

在這個範例中,我們將一個 input 元素的參考存儲在 myRef 中。當按鈕被點擊時,我們可以透過 myRef.current 來訪問這個 input 元素,並將焦點設置到它上面。

總的來說,useRef 提供了一個方便的方式來在函式型組件中存儲和訪問 DOM 元素或其他 React 元素,並且也可以用於存儲任何可變的值。

https://ithelp.ithome.com.tw/upload/images/20230929/201357504mqoNU4XC1.png

當然,使用 useRef 時有一些需要注意的地方:

  1. 初始化值: 如果你希望 useRef 在創建時具有一個初始值,你可以像這樣使用 useRef(initialValue)。這個初始值會在組件的整個生命週期中保持不變。

    const myRef = useRef(initialValue);
    
  2. 當組件重新渲染時,useRef 不會改變: useRef 的返回值(也就是它的 .current 屬性)在組件重新渲染時保持不變。這使得它成為存儲可以保留在渲染之間的值的良好選擇。

  3. 避免直接操作 DOM: 一般來說,盡量避免直接操作 DOM。React 通常會自動管理 DOM,直接操作 DOM 可能會破壞 React 的預期行為。

  4. 不要將 useRef 與 useState 混淆: useRef 是用來保存可變的引用的,而 useState 是用來保存組件的狀態的。

  5. 不要在渲染過程中依賴 useRef 的值: 由於 useRef 的值在渲染過程中不會改變,因此不要在渲染過程中依賴它的值進行邏輯判斷。

    function MyComponent() {
      const myRef = useRef(0);
    
      // 這裡的 myRef.current 將永遠是 0
      if (myRef.current === 0) {
        // ...
      }
    
      return <div>這是一個 DOM 元素</div>;
    }
    

總的來說,useRef 是一個非常有用的 hooks,但需要小心使用它,確保遵守 React 的最佳實踐,並避免直接操作 DOM。

https://ithelp.ithome.com.tw/upload/images/20230929/20135750saO4VzdQs2.png

當你需要使用 useRef 的情境並不限於直接操作 DOM。以下是一些使用 useRef 的其他情境和相應的範例:

  1. 保存上一次渲染的值: 如果你想要比較前一次和當前渲染的值,可以使用 useRef 來保存上一次的值,這在 useEffect 中非常有用。

    import React, { useEffect, useRef } from 'react';
    
    function MyComponent() {
      const prevValueRef = useRef(null);
      const value = 42;
    
      useEffect(() => {
        prevValueRef.current = value;
        console.log(`上一次的值是:${prevValueRef.current}`);
      });
    
      return <div>當前值是:{value}</div>;
    }
    
  2. 觸發非受控元素的操作: 在某些情況下,你可能需要觸發非受控元素(如原生 JavaScript 或第三方庫中的元素)的操作。使用 useRef 來保存對這些元素的引用可以方便地執行這些操作。

    import React, { useRef } from 'react';
    
    function MyComponent() {
      const inputRef = useRef(null);
    
      const handleButtonClick = () => {
        // 觸發 input 元素的選取操作
        inputRef.current.select();
      };
    
      return (
        <div>
          <input ref={inputRef} type="text" defaultValue="文本內容" />
          <button onClick={handleButtonClick}>選取文本</button>
        </div>
      );
    }
    
  3. 儲存計時器的 ID: 如果你需要管理計時器,例如在組件卸載時清除計時器,可以使用 useRef 來儲存計時器的 ID。

    import React, { useEffect, useRef } from 'react';
    
    function TimerComponent() {
      const timerIdRef = useRef(null);
    
      useEffect(() => {
        // 啟動計時器
        timerIdRef.current = setInterval(() => {
          console.log('計時中...');
        }, 1000);
    
        // 在組件卸載時清除計時器
        return () => {
          clearInterval(timerIdRef.current);
        };
      }, []);
    
      return <div>計時器組件</div>;
    }
    

這些情境示例展示了 useRef 在 React 中的多種應用方式,不僅僅限於操作 DOM。它可以用於管理狀態,執行非受控元素的操作,保存之前的值等等。請根據你的具體需求來使用它。

看了這麼多useRef的例子,最後還是要探究根源一下

這個ref指的到底是什麼

在 React 中,ref 是一種特殊的屬性,它用來建立對 DOM 元素或 React 元素的引用。ref 的名稱來源於 "reference"(引用),它允許你在 React 組件中訪問、操作或管理特定元素的方式。

主要的用途包括:

  1. 訪問 DOM 元素: 你可以使用 ref 來建立對特定 DOM 元素的引用,然後在需要的時候訪問和操作這些元素。這在需要直接操作 DOM 時非常有用。
  2. 訪問 React 元素: 你也可以使用 ref 來建立對其他 React 組件的引用,然後透過這些引用來調用該組件的方法或訪問其狀態。
  3. 儲存可變值: ref 也可以用來儲存可變的值,並在組件的多次渲染之間保持不變。這對於記錄之前的值或進行一些特殊的邏輯非常有用。

簡而言之,ref 允許你在 React 中建立對元素的引用,以便在需要時可以更方便地訪問和操作這些元素,或者執行一些特殊的操作。請注意,直接操控 DOM 的情況下應謹慎使用 ref,盡量遵循 React 的組件化和狀態管理模式。

不出意外的話,就要出意外了 - 剛學useRef就翻車

開始已知用火,學會在必要的時候使用Ref來操控DOM之後…
假設今天我們自己做了了一個和input相關的Component
比如說長這樣:一個紅色邊框的input (先不管value或是onChange)

function MyInput(){
    return (
        <input style={{border: '1px red solid'}}/>
    )
}

然後外層用了一個Component引入他,並把Ref給這個MyInput

function MyComponent(){
    const myRef = useRef(null)
    function handleClick(){
        myRef.current.focus()
    }
    return(
        <div>
            <button onClick={handleClick}>點我focus</button>
            <MyInput ref={myRef}/>
        </div>
    )
}

興高采烈地準備跑起來,卻發現完全不能動,還噴了錯誤訊息出來
https://ithelp.ithome.com.tw/upload/images/20230929/20135750RTNUPnkfRR.png

來試試沒有什麼特別技巧的,單純把整個問題描述丟給ChatGPT
https://ithelp.ithome.com.tw/upload/images/20230929/20135750hHWG4ApKgO.png

在 React 中,要將 ref 傳遞到自定義組件中,你需要使用 React.forwardRef 來將 ref 正確地傳遞到內部的 input 元素中。

以下是你的程式碼經過修正:

import React, { useRef, forwardRef, useImperativeHandle } from 'react';

const MyInput = forwardRef((props, ref) => {
    const inputRef = useRef(null);

    useImperativeHandle(ref, () => ({
        focus: () => {
            inputRef.current.focus();
        }
    }));

    return (
        <input style={{border: '1px red solid'}} ref={inputRef} />
    )
});

function MyComponent() {
    const myRef = useRef(null);

    function handleClick() {
        myRef.current.focus();
    }

    return (
        <div>
            <button onClick={handleClick}>點我focus</button>
            <MyInput ref={myRef} />
        </div>
    )
}

export default MyComponent;

這裡做了幾個變更:

  1. 使用 React.forwardRef 包裹了 MyInput 組件,這樣可以接收 ref 作為第二個參數。
  2. 在 MyInput 組件中,建立了一個 inputRef 來引用 input 元素。
  3. 使用 useImperativeHandle 定義了 ref 對外暴露的方法 focus,當呼叫 myRef.current.focus() 時,會觸發 inputRef.current.focus()。
  4. 將 inputRef 傳遞給 input 元素的 ref 屬性。

現在,你可以在 MyComponent 中正確地使用 myRef.current.focus() 來聚焦到 input 元素了。

突然跑出了新的語法!
React.forwardRef 和 useImperativeHandle

逐一來了解這兩個語法的用法與介紹

React.forwardRef

https://ithelp.ithome.com.tw/upload/images/20230929/20135750NmycI0Tfkv.png

當然,現在讓我來為你提供 React.forwardRef 的相關教學和介紹:

使用 React.forwardRef:

React.forwardRef 是 React 提供的一個方法,用於傳遞 ref 到子組件。它允許子組件接收 ref 作為第二個參數(props),然後將這個 ref 傳遞給內部的 DOM 元素或其他 React 組件。

以下是一個簡單的示例:

import React, { forwardRef } from 'react';

const MyComponent = forwardRef((props, ref) => {
  return <input ref={ref} />;
});

export default MyComponent;

如何使用 React.forwardRef:

  1. 在子組件中使用 forwardRef:

    首先,在子組件中使用 forwardRef 來包裹組件定義,並在函式的參數中接收 props 和 ref。

    const MyComponent = forwardRef((props, ref) => {
      return <input ref={ref} />;
    });
    
  2. 將 ref 傳遞到內部的 DOM 元素或其他 React 組件:

    在子組件的 JSX 中,將 ref 傳遞給需要引用的 DOM 元素或其他 React 組件。

    return <input ref={ref} />;
    
  3. 在父組件中使用 ref:

    在父組件中可以正常地將 ref 傳遞給子組件。

    const myRef = useRef(null);
    
    return <MyComponent ref={myRef} />;
    

注意事項:

  • forwardRef 接收一個函式,這個函式接收兩個參數:props 和 ref。在這個函式中,你可以返回 JSX 或 React 元素。
  • forwardRef 是一個高階組件(Higher-Order Component,HOC)工具,它使得傳遞 ref 到子組件變得容易。
  • 使用 forwardRef 時,你需要記得將接收到的 ref 傳遞到子組件的 DOM 元素或其他 React 組件上。

總的來說,React.forwardRef 提供了一個簡潔的方式來在 React 中傳遞 ref,使得你可以更容易地在父組件中操作子組件中的 DOM 元素或 React 組件。

嗯嗯,也就是說可以把外部接收的ref給正確的傳遞到Component內
但ChatGPT介紹React.forwardRef的回答中
並沒有使用到他先前用到的useImperativeHandle

於是繼續追問
https://ithelp.ithome.com.tw/upload/images/20230929/20135750CbfYbhmigD.png

useImperativeHandle 是 React 提供的另一個 hooks,它用於自定義 ref 對外暴露的接口。這樣可以控制父組件中的 ref.current 被訪問時可以看到哪些屬性或方法。

在你的情境中,當你想要讓父組件可以透過 ref 來直接調用子組件的特定方法(例如 focus())時,可以使用 useImperativeHandle。

以下是對你之前提供的程式碼進一步解釋:

import React, { useRef, forwardRef, useImperativeHandle } from 'react';

const MyInput = forwardRef((props, ref) => {
    const inputRef = useRef(null);

    // 使用 useImperativeHandle 定義對外暴露的方法 focus
    useImperativeHandle(ref, () => ({
        focus: () => {
            inputRef.current.focus();
        }
    }));

    return (
        <input style={{border: '1px red solid'}} ref={inputRef} />
    )
});

這裡的 useImperativeHandle 的作用是將一個對外的方法 focus 添加到了 ref 對象上,當父組件使用 myRef.current.focus() 時,實際上會調用子組件中 inputRef.current.focus()。

要使用 useImperativeHandle,你需要注意以下幾點:

  1. useImperativeHandle 需要在 forwardRef 中使用,因為它需要接收第二個參數 ref。
  2. 在 useImperativeHandle 內部,你可以定義一個對外暴露的方法(例如 focus()),這樣父組件就可以透過 ref.current 調用這個方法。

總的來說,useImperativeHandle 可以用來精確地控制父組件可以訪問子組件中的哪些方法或屬性,提高了程式碼的可讀性和可維護性。

理解一下,所以useImperativeHandle 指定了外部只能用dom的哪些方法
和ChatGPT確認一下我的理解是否正確
https://ithelp.ithome.com.tw/upload/images/20230929/20135750St0b2ndwmO.png

正確,當你使用了 useImperativeHandle 來定義 ref 對外暴露的方法後,外部的 ref 就只能操控子組件中定義的那些方法,如上例中的 focus。

如果你希望在 ref 對象上添加更多的方法或屬性,你可以在 useImperativeHandle 的回調函式中返回更多的屬性或方法。

以下是一個範例,假設你想要在 MyInput 中添加一個名為 getValue 的方法,讓父組件可以獲取 input 元素的值:

const MyInput = forwardRef((props, ref) => {
    const inputRef = useRef(null);

    useImperativeHandle(ref, () => ({
        focus: () => {
            inputRef.current.focus();
        },
        getValue: () => {
            return inputRef.current.value;
        }
    }));

    return (
        <input style={{border: '1px red solid'}} ref={inputRef} />
    )
});

現在,在父組件中,你可以使用 myRef.current.getValue() 來獲取 input 元素的值。

總結來說,你可以在 useImperativeHandle 的回調函式中返回任何你希望在 ref 對象上公開的屬性或方法,這樣父組件就可以根據需要來訪問子組件的相關功能。

我是叛逆的壞小孩,我想知道useImperativeHandle 是否可以自行選擇要不要使用
https://ithelp.ithome.com.tw/upload/images/20230929/201357509ZUk4uLcGh.png

是的,當你使用 React.forwardRef 沒有使用 useImperativeHandle,父元件可以直接調用子元件的 DOM 操作或者存取其數值。這是因為 forwardRef 預設會將 ref 傳遞給子元件。

以下是一個範例程式碼:

import React, { forwardRef } from 'react';

const MyInput = forwardRef((props, ref) => {
    return (
        <input style={{border: '1px red solid'}} ref={ref} />
    )
});

function MyComponent() {
    const myRef = useRef(null);

    function handleClick() {
        myRef.current.focus(); // 可以直接調用 input 元素的 focus 方法
        console.log(myRef.current.value); // 可以直接獲取 input 元素的值
    }

    return (
        <div>
            <button onClick={handleClick}>點我 focus</button>
            <MyInput ref={myRef} />
        </div>
    )
}

export default MyComponent;

在這個範例中,我們沒有使用 useImperativeHandle,但是父元件 MyComponent 仍然可以直接調用子元件 MyInput 中 input 元素的 focus 方法以及存取其值。這是因為 forwardRef 預設會將 ref 傳遞給子元件。

請注意,這種情況下父元件可以直接訪問子元件的所有 DOM 操作和數值,但這樣的寫法可能會使程式碼耦合度增加,請謹慎使用,保持良好的程式設計實踐。

OK! 所以不使用也是可以的,只是有可能會暴露過多
又回到使用原生JS或jQuery時毫無規範的窘境,變得混亂不堪
內部完全不知道外面會對這個元件進行什麼操作或更變,要追起來十分困難

統整章節內容

https://ithelp.ithome.com.tw/upload/images/20230929/20135750ruUGchnxJy.png

在這次的對話中,我們探討了以下重要概念:

  1. useRef 的使用: useRef 是 React 提供的 hooks,用於創建可變的物件參考,主要用途包括訪問 DOM 元素、訪問 React 元素、儲存可變值等。它可以幫助你在函式型組件中管理狀態和 DOM 操作。
  2. React.forwardRef 的使用: React.forwardRef 是用來傳遞 ref 到子組件的方法,它允許你在子組件中接收 ref 作為第二個參數,然後將這個 ref 傳遞給內部的 DOM 元素或其他 React 組件。這在自定義組件中使用 ref 時非常有用。
  3. useImperativeHandle 的使用: useImperativeHandle 是 React 提供的 hooks,它用於自定義 ref 對外暴露的接口,使得父組件可以根據需要訪問子組件中的特定方法或屬性。這對於控制 ref 的外部介面非常有用。

總結來說,React 提供了一個強大的機制,讓你可以更容易地在組件之間傳遞 ref,訪問和操作 DOM 元素,以及自定義 ref 的對外介面。然而,使用這些功能時需要謹慎,遵循最佳實踐,以確保你的程式碼保持可讀性和可維護性。

最後,請記住,React 是一個強大且靈活的庫,它提供了多種方式來處理不同的場景,請根據你的具體需求選擇適合的方法。希望這次的討論對你有所幫助,祝你在 React 開發中取得成功!

結語

Hook的介紹大概就到這邊告一個段落

本次和ChatGPT的對話紀錄:
https://chat.openai.com/share/8083b95b-eba7-4234-aa83-e246b727f20c

下個章節要講 React & TypeScript了!

為了簡化介紹,後來我請ChatGPT先不要給TypeScript版本的
不然語法上看起來好像很複雜

如果是跟著我從Day15建置環境開始,就使用typescript
建立檔案都是使用.tsx而不是.jsx的話

可能這個章節會微微..微..地發現上面的範例沒辦法直接跑
會有一些type上的限制與警告,像是說myRef.current 可能是null或undefined之類的

想要先讓他跑起來,就是各種補any還有?.
例如:myRef.current?.focus() 、 const myRef = useRef<any>(null) 等等

詳細會到下個章節一起介紹!

我是期待回到TypeScript懷抱的前端工程師一宵三筵,我們明天見!


上一篇
【Day19】ChatGPT請教教我:React入門(五)- 更多Hook!useMemo!useCallback!自定義Hook!
下一篇
【Day21】ChatGPT請教教我:React入門(七)- React & TypeScript !
系列文
用ChatGPT詠唱來完成工作與點亮前後端技能樹 共 56 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言